﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using SewerAPI.Util;

namespace SewerAPI.IO
{
    /// <summary>
    /// Class to handle the received bytes from the UDP client.
    /// </summary>
    public class Client : TransactionHandler, IDisposable
    {
        #region Private Fields

        UdpClient client;
        IPEndPoint endpoint = new IPEndPoint(IPAddress.Any, Configuration.Port);
        bool allowlistening = true;
        event BytesReceived onbytesreceived;

        #endregion

        #region Public Fields

        /// <summary>
        /// On bytes received handler
        /// </summary>
        public event BytesReceived OnBytesReceived
        {
            add { onbytesreceived += value; }
            remove { onbytesreceived -= value; }
        }

        #endregion

        public Client() : base()
        {

        }

        #region Abstract Methods

        protected override void PerformTransaction(Core.Message m)
        {
            //TODO: Implement this function
        }

        protected override void InitializeHandler()
        {
            SewerLogger.Instance.Info("Starting client on port {0}", Configuration.Port);
            client = new UdpClient(Configuration.Port);
            client.EnableBroadcast = true;
        }



        public void StopListening()
        {
            SewerLogger.Instance.Info("Stop listening");
            //Set the flag to false;
            allowlistening = false;
        }

        public void StartListening()
        {
            SewerLogger.Instance.Info("Start listening");
            //Set the flag to true
            allowlistening = true;
            //Start receiving
            client.BeginReceive(new AsyncCallback(receive), null);
        }

        private void receive(IAsyncResult result)
        {
            if (allowlistening)
            {
                //End the asynchronous call
                byte[] _data = client.EndReceive(result, ref endpoint);
                //Launch the callback for this method
                if (onbytesreceived != null)
                {
                    onbytesreceived(_data, result);
                }
                //Try to decrypt it
                try
                {
                    string _channel = Configuration.EncryptionEngine.DecryptChannel(_data);
                    if (!string.IsNullOrEmpty(_channel))
                    {
                        Transact(Configuration.EncryptionEngine.DecryptMessage(_data));
                    }
                    else
                    {
                        SewerLogger.Instance.Debug("Cannot receive this message from {0}", endpoint.ToString());
                    }
                    
                }
                catch (Exception e)
                {
                    SewerLogger.Instance.Debug("Cannot receive this message from {0}", endpoint.ToString(), e);
                }
            }
        }

        #endregion

        public void Dispose()
        {
            if (client != null)
            {
                try
                {
                    client.Close();
                }
                catch { }
            }
        }
    }
}
